import sys
import time
from datetime import datetime
from typing import Optional

from bson import ObjectId
from bson.errors import InvalidId
from loguru import logger
from pydantic import Field

from api.dataapi.task import task_activity_contacts
from ioservice.activity_register import aggregate_activity_registers_raw
from ioservice.alumni.alumni_reward import drop_alumni_from_rewards
from ioservice.common import mdb
from ioservice.common.mongo import aggregate_models, count_documents as mongo_count_docs, find_many_docs, \
    find_many_models, delete_models, fake_delete_models, fake_delete_models_by_ids
from ioservice.const import activity_col, institution_col, activity_register_col, alumni_col
from ioservice.institution import sync_institutions_data
from models.alumni.alumni import Alumni
from models.alumni_extra import AlumniExtraDataSearchModel, AlumniInstitutionEnum, AlumniRewardEnum, \
    AlumniExtraDataModel
from models.common import MongoModel, OID
from schemas.search import SearchResult
from task_utils.task_es import job_delete_alumni, job_update_activity
from task_utils.task_jobs import job_join_act_contacts, job_update_ranking
from utils.regionutil import province2area


class AlumniWithActivity(MongoModel):
    id: Optional[OID] = Field(title="校友ID")
    activity_name: Optional[str] = Field(title="活动名称")
    start_time: Optional[datetime] = Field(title="活动开始时间")
    expert_info: Optional[Alumni] = Field(title="校友信息")


async def get_recent_active_alumni():
    piplines = [
        {"$match": {"start_time": {"$exists": True, "$ne": None}, "is_deleted": {"$ne": True}}},
        {"$sort": {"start_time": -1}},
        {
            "$lookup": {
                "from": "activity_register",
                "localField": "_id",
                "foreignField": "activity_id",
                "as": "registered"
            }
        },
        {
            "$lookup": {
                "from": "alumni",
                "localField": "registered.person_id",
                "foreignField": "_id",
                "as": "alumni"
            }
        },
        {
            "$unwind": "$alumni"
        },
        {
            "$group": {
                "_id": "$alumni._id",
                "activity_name": {"$first": "$name"},
                "start_time": {"$first": "$start_time"},
                "alumni": {"$first": "$alumni"}
            }
        },
        {"$sort": {"start_time": -1}},
        {
            "$limit": 10
        }
    ]
    result = await aggregate_models(activity_col, piplines, AlumniWithActivity)
    return result


async def get_alumni_extra_data(_id, context: AlumniExtraDataSearchModel):
    data_col, data_model, query = context.compose_mongo_query()
    params = context.parameters
    query.update({"person_id": ObjectId(_id), "is_deleted": {"$ne": True}})
    total = await mongo_count_docs(data_col, query)
    res = await find_many_models(data_col, data_model, query, sort=params.sort, offset=params.offset,
                                 size=params.size, fields=params.fields)
    return SearchResult(items=res, total=total)


def check_oid(id_str):
    try:
        ObjectId(id_str)
        return True
    except InvalidId:
        return False


async def parse_alumni_agg_results(aggregation):
    if aggregation.get("institution"):
        institution_ids = [ObjectId(r["_id"]) for r in aggregation["institution"] if
                           r.get("_id") and check_oid(r["_id"])]

        if institution_ids:
            institution_names = await find_many_docs(institution_col,
                                                     {"_id": {"$in": institution_ids}, "is_deleted": {"$ne": True}},
                                                     projection=["institution_name", "_id"])
            institution_names = {str(ins["_id"]): ins.get("institution_name", None) for ins in institution_names}
            cur_institutions = []
            for institution in aggregation["institution"]:
                institution_id = institution["_id"]
                if not institution_names.get(institution_id):
                    continue
                institution["_id"] = institution_names[institution_id]
                cur_institutions.append(institution)
            aggregation["institution"] = cur_institutions
    aggregation = [{key: value} for key, value in aggregation.items()]
    return aggregation


async def parse_alumni_search_results(alumni, context):
    """
    用于转换校友搜索中的组织信息,并排序
    """
    # for alumnus in alumni:
    #
    #     if alumnus.get("native_place", {}).get("province"):
    #         alumnus["native_place"]["area"] = province2area.get(alumnus.get("native_place", {}).get("province"), None)

    if not context.fields or "cur_institution_name" in context.fields:
        alumni_institutions = [p["cur_institution_id"] for p in alumni if p.get("cur_institution_id")]
        if alumni_institutions:
            institutions = await find_many_docs(institution_col, {"_id": {"$in": alumni_institutions}},
                                                projection=["institution_name", "_id"], sort=context.sort)
            institutions = {str(ins["_id"]): {"counter": counter, "name": ins["institution_name"]} for counter, ins in
                            enumerate(institutions)}
            for p in alumni:
                if p.get("cur_institution_id") and institutions.get(p["cur_institution_id"], {}).get("name", ""):
                    p["cur_institution_name"] = institutions.get(p["cur_institution_id"], {}).get("name", "")
            alumni.sort(key=lambda x: institutions.get(x.get("cur_institution_id"), {}).get("counter", sys.maxsize))


async def delete_alumni_extra_info(person_ids):
    # 假删除机构链接
    institution_fields = AlumniInstitutionEnum._member_names_
    params = {"query": {"person_id": {"in": [ObjectId(_id) for _id in person_ids]}}}
    for info_field in institution_fields:
        alumni_extra_model = AlumniExtraDataSearchModel(data_model=info_field, parameters=params)
        data_col, _, query = alumni_extra_model.compose_mongo_query()
        await fake_delete_models(data_col, query)

    # 删除成果链接
    reward_fields = AlumniRewardEnum._member_names_
    for info_field in reward_fields:
        alumni_extra_model = AlumniExtraDataModel(data_model=info_field)
        data_col = alumni_extra_model.set_data_col()
        await drop_alumni_from_rewards(data_col, person_ids, [], drop_all=True)


async def drop_alumni_attended_activities_record(person_ids):
    person_query = {"person_id": {"$in": [ObjectId(_id) for _id in person_ids]}}
    res = await delete_models(activity_register_col, person_query)
    return res


async def find_alumni_attended_activities_and_institutions(person_ids):
    query = [
        {
            "$match": {
                "person_id": {"$in": [ObjectId(_id) for _id in person_ids]}, "is_deleted": {"$ne": True}
            }
        },
        {
            "$group": {
                "_id": "$activity_id"
            }
        },
        {
            "$lookup": {
                "from": "activity",
                "let": {"act_id": "$_id"},
                "pipeline": [
                    {"$match": {"$expr": {"$and": [
                        {"$eq": ["$_id", "$$act_id"]},
                        {"$ne": ["$is_deleted", True]},
                    ]}}},
                    {"$project": {"institution_id": True}}
                ],
                "as": "activity"
            },
        },
        {
            "$unwind": "$activity"
        },
        {
            "$group": {
                "_id": "$activity.institution_id",
                "activity_id": {"$first": "$_id"}
            }
        }
    ]
    attended = await aggregate_activity_registers_raw(query)
    attended_institutions, attended_activities = set(), set()
    for record in attended:
        attended_institutions.add(record["_id"])
        attended_activities.add(record["activity_id"])
    return list(attended_activities), list(attended_institutions)


async def drop_alumni(person_ids):
    """
    通过person_id删除校友

    2021.04.02 删除校友时删除所有附属信息
    """
    res = await fake_delete_models_by_ids(alumni_col, person_ids)
    col = mdb.get_collection(activity_register_col)
    act_ids = await col.find({"person_id": {"$in": [ObjectId(p) for p in person_ids]}, "is_attend": True}).distinct(
        "activity_id")
    # 更新活动的报名人次
    if act_ids:
        for act_id in act_ids:
            task_activity_contacts(str(act_id))

    # 删除活动参与
    attended_activities, attended_institutions = await find_alumni_attended_activities_and_institutions(person_ids)
    await drop_alumni_attended_activities_record(person_ids)
    # 删除校友附属信息
    # await delete_alumni_extra_info(person_ids)
    # # 更新机构服务校友数量(从校友参加活动中获取机构)
    # await sync_institutions_data(attended_institutions)
    #
    # # TODO 更新活动中人数统计
    # await batch_update_computing_fields(person_ids, 'drop_alumni')
    return res
